package web

import (
	"errors"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"log"
	"net/http"
	"ota-go/database"
	"ota-go/utils"
	"path/filepath"
	"strings"
)

type Controller struct {
	DB       *gorm.DB
	TokenMap map[string]database.User
}

func CreateController(db gorm.DB) *Controller {
	controller := Controller{
		DB:       &db,
		TokenMap: make(map[string]database.User),
	}
	return &controller
}

// CheckUserLoginState 检查用户登录状态
func (controller *Controller) CheckUserLoginState(c *gin.Context) (*database.User, error) {
	session := sessions.Default(c)
	token := session.Get("token")
	if token == nil || token == "" {
		return nil, errors.New("当前没有登录1")
	}
	log.Println("token:", token)
	if user, ok := controller.TokenMap[token.(string)]; ok {
		var queryUser database.User
		err := controller.DB.Model(&database.User{}).Where("id=?", user.ID).First(&queryUser).Error
		if err != nil {
			return nil, err
		}
		return &queryUser, nil
	}
	return nil, errors.New("当前没有登录2")
}

// Login 登录
func (controller *Controller) Login(c *gin.Context) {
	session := sessions.Default(c)
	var uploadUser database.User
	err := ParserRequestData[database.User](c, &uploadUser)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  "输入用户名和密码有误",
		})
		return
	}
	var user database.User
	tx := controller.DB.Where("user_name = ?", uploadUser.UserName).First(&user)
	if tx.Error != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  "用户不存在",
		})
		return
	}
	if uploadUser.Password != user.Password {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  "密码错误",
		})
		return
	}
	token := session.Get("token")
	if token == nil || token == "" {
		token = uuid.New().String()
		session.Set("token", token)
		err = session.Save()
	}
	controller.TokenMap[token.(string)] = user
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  "登录失败",
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "成功",
	})
}

func (controller *Controller) GetUserInfo(c *gin.Context) {
	user, err := controller.CheckUserLoginState(c)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Data: user,
	})
}

// Logout 注销退出登录
func (controller *Controller) Logout(c *gin.Context) {
	_, err := controller.CheckUserLoginState(c)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	session := sessions.Default(c)
	token := session.Get("token")
	if token != nil && token != "" {
		delete(controller.TokenMap, session.Get("token").(string))
	}
	session.Clear()
	session.Options(sessions.Options{Path: "/", MaxAge: -1})
	_ = session.Save()
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "退出登录成功",
	})
}

func (controller *Controller) UpdateUserInfo(c *gin.Context) {
	user, err := controller.CheckUserLoginState(c)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	var dataMap map[string]interface{}
	err = ParserRequestData[map[string]interface{}](c, &dataMap)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	var updateCount uint8
	userName, err := utils.GetMapValue[string](dataMap, "UserName")
	if err == nil {
		user.UserName = *userName
		updateCount += 1
	}
	password, err := utils.GetMapValue[string](dataMap, "Password")
	if err == nil {
		user.Password = *password
		updateCount += 1
	}
	if updateCount <= 0 {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  "没有修改任何信息",
		})
		return
	}
	err = controller.DB.Model(database.User{}).Where("id=?", user.ID).Updates(user).Error
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "success",
	})
}

// SubmitOtaUpdateInfo 提交ota升级
func (controller *Controller) SubmitOtaUpdateInfo(c *gin.Context) {
	var otaInfo database.OtaInfo
	err := ParserRequestData[database.OtaInfo](c, &otaInfo)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	if strings.TrimSpace(otaInfo.SerialNumber) != "" {
		var count int64
		controller.DB.Model(&database.OtaInfo{}).Where("serial_number=?", otaInfo.SerialNumber).Count(&count)
		if count > 0 {
			tx := controller.DB.Model(&database.OtaInfo{}).Where("serial_number=?", otaInfo.Type).Updates(&otaInfo)
			if tx.Error != nil {
				c.JSON(http.StatusOK, Result{
					Code: FAIL,
					Msg:  tx.Error.Error(),
				})
				return
			}
			c.JSON(http.StatusOK, Result{
				Code: SUCCESS,
				Msg:  "success",
			})
			return
		}
		tx := controller.DB.Create(&otaInfo)
		if tx.Error != nil {
			c.JSON(http.StatusOK, Result{
				Code: FAIL,
				Msg:  tx.Error.Error(),
			})
			return
		}
		c.JSON(http.StatusOK, Result{
			Code: SUCCESS,
			Msg:  "success",
		})
		return
	}

	tx := controller.DB.Create(&otaInfo)
	if tx.Error != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  tx.Error.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "success",
	})
}

// GetOtaUpdateInfos 获取ota更新信息列表
func (controller *Controller) GetOtaUpdateInfos(c *gin.Context) {
	var dataMap map[string]interface{}
	err := ParserRequestData[map[string]interface{}](c, &dataMap)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	page, err := utils.GetMapValue[float64](dataMap, "page")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	pageSize, err := utils.GetMapValue[float64](dataMap, "pageSize")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	var total int64
	if err = controller.DB.Model(database.User{}).Count(&total).Error; err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	offset := int((*page - 1) * (*pageSize))
	var otaUpdateInfos []database.OtaInfo
	if err = controller.DB.Limit(int(*pageSize)).Offset(offset).Find(&otaUpdateInfos).Error; err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "success",
		Data: otaUpdateInfos,
	})
}

func (controller *Controller) GetOtaUpdateInfo(c *gin.Context) {
	var dataMap map[string]interface{}
	err := ParserRequestData[map[string]interface{}](c, &dataMap)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	id, err := utils.GetMapValue[float64](dataMap, "id")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	var otaUpdateInfo database.OtaInfo
	err = controller.DB.Model(database.OtaInfo{}).Where("id=?", int(*id)).Find(&otaUpdateInfo).Error
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "success",
		Data: otaUpdateInfo,
	})
}

func (controller *Controller) DeleteOtaUpdateInfo(c *gin.Context) {
	var dataMap map[string]interface{}
	err := ParserRequestData[map[string]interface{}](c, &dataMap)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	id, err := utils.GetMapValue[float64](dataMap, "id")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	err = controller.DB.Model(database.OtaInfo{}).Delete("id=?", int(*id)).Error
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "success",
	})
}

func (controller *Controller) CheckOtaUpdate(c *gin.Context) {
	var dataMap map[string]interface{}
	err := ParserRequestData[map[string]interface{}](c, &dataMap)
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	all, err := utils.GetMapValue[string](dataMap, "All")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	serialNumber, err := utils.GetMapValue[string](dataMap, "SerialNumber")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	versionCode, err := utils.GetMapValue[float64](dataMap, "VersionCode")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	otaType, err := utils.GetMapValue[float64](dataMap, "Type")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	var otaUpdateInfo database.OtaInfo
	err = controller.DB.Model(database.OtaInfo{}).Where("serial_number=? and type=?", *serialNumber, *otaType).Find(&otaUpdateInfo).Error
	if err != nil {
		err = controller.DB.Model(database.OtaInfo{}).Where("all=? and type=?", *all, *all).Find(&otaUpdateInfo).Error
		if err != nil {
			c.JSON(http.StatusOK, Result{
				Code: FAIL,
				Msg:  err.Error(),
			})
			return
		}
	}
	if otaUpdateInfo.VersionCode >= int(*versionCode) {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  "没有新版本",
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "有新版本!",
		Data: otaUpdateInfo,
	})
}

func (controller *Controller) UploadFile(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	ext := strings.ToLower(filepath.Ext(file.Filename))
	fileName := uuid.New().String() + ext
	savePath := "./uploads/" + fileName
	if err := c.SaveUploadedFile(file, savePath); err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Msg:  "success",
		Data: fileName,
	})
}

func (controller *Controller) GetAllUploadFiles(c *gin.Context) {
	files, err := utils.ListFiles("./uploads")
	if err != nil {
		c.JSON(http.StatusOK, Result{
			Code: FAIL,
			Msg:  err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, Result{
		Code: SUCCESS,
		Data: files,
	})
}
